import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.regex.Pattern;

import javax.xml.parsers.ParserConfigurationException;

import org.xml.sax.SAXException;

/**
 * The Library command line console.
 * 
 * @author Gadi Elishayov - 026540005
 * @author Ruby Fienstein - 310211074
 */
public class LibraryConsole {

	private static InputStreamReader converter = new InputStreamReader(System.in);
	private static BufferedReader in = new BufferedReader(converter);
	
	/**
	 * The main function 
	 * 
	 * @param args meaningless
	 * 
	 * @throws IOException
	 * @throws SAXException 
	 * @throws ParserConfigurationException 
	 */
	public static void main(String[] args) throws IOException, ParserConfigurationException, SAXException {

		Library library = new Library();

		for (Book b : BookListParser.parseBookList("books.xml"))
			library.addBook(b);

		while (true) {
			String rawCommand = readCommand();
			// Check exit command
			if(rawCommand==null || rawCommand.equalsIgnoreCase("exit")){
				System.out.println("Bye!");
				break;
			}
			handleCommand(library,rawCommand);
		}
		
	}
	
	/**
	 * Reads one command from the user
	 * NOTE: empty commands are ignored
	 * NOTE: commands that starts with '#' are ignored and treated as comments 
	 * 
	 * @return the string of the unparsed command
	 */
	private static String readCommand() throws IOException {
		System.out.print("Enter command: ");
		String s = "";
		while(s.equals("")){
			s = in.readLine();
			// Check for EOF
			if(s==null)
				break;
			// Check comments
			else if(s.length()>0 && s.startsWith("#"))
			{
				// Print the comment
				System.out.println(s);
				s = "";
				continue;
			}				
		}
		return s;
	}

	/**
	 * Handles one commands
	 * 
	 * @param library
	 * @param rawCommand unparsed command
	 */	
	private static void handleCommand(Library library, String rawCommand) {

		// Split command to different parts
		String[] command = rawCommand.split(" ");
		
		// Sanity check
		if (command.length < 2 || command.length > 4) {
			System.out.println("Invalid command");
			return;
		}

		// Handle add(+) commands
		if (command[0].equals("+"))
			handleAddCommand(library, command);
        // Handle remove(-) commands				
		else if (command[0].equals("-"))
			handleRemoveCommand(library, command);
		// Handle query(?) commands 
		else if (command[0].equals("?"))
			handleQueryCommands(library, command);
		else
			System.out.println("Invalid command");
	}

	/**
	 * Handles query('?') command
	 *  - print subscribers with most books
	 *    example: '? !'
	 *  - print current book owner
	 *    example: '? AX1234'
	 *  - print list of books borrowed by subscriber
	 *    example: '? 301211074'
	 * 
	 * @param library
	 * @param command  
	 */	
	private static void handleQueryCommands(Library library, String[] command) {
		// Handle get subscribers with most books				
		if (command.length == 2 && command[1].equals("!")) {
			
			/*
			 * Example: ? !
			 */
			RealLinkedList subscribers = library.getSubscribersWithMostBooks();
			if (subscribers.size() == 0) {
				System.out.println("Nobody borrowed books!");
			} else {
				System.out.println("Subscribers with most books:");
				Subscriber sub = (Subscriber) subscribers.head();
				while (sub != null) {
					System.out.println(sub);
					sub = (Subscriber) sub.next;
				}
			}
		// Handle get book owner query				
		} else if (command.length == 2 && isBookCode(command[1])) {
			
			/*
			 * Example: ? AX2713
			 */
			String bookId = command[1];
			try {
				Subscriber subscriber = library.getBookOwner(bookId);
				if (subscriber != null)
					System.out.println("Book owner: " + subscriber);
				else {
					System.out.println("Book has no owner");
				}
			} catch (IllegalArgumentException e) {
				System.out.println("Invalid book code");
			}
			// Handle get book list for subscriber query				
		} else if (command.length == 2 && !isBookCode(command[1])) {
			
			/*
			 * Example: ? 112540783
			 */
			String subscriberId = command[1];
			try {
				RealLinkedList books = library.getBooks(subscriberId);
				if (books.size() == 0) {
					System.out.println("this subscriber doesn't hold books");
				} else {
					System.out.println("Book list for subscriber: " + subscriberId);
					Book currentBook = (Book) books.head();
					while (currentBook != null) {
						System.out.println(currentBook);
						currentBook = (Book) currentBook.next;
					}
				}
			} catch (IllegalArgumentException e) {
				System.out.println("Invalid subscriber id");
			}
		}
	}

	/**
	 * Handles remove('-') command
	 *  - return specified book
	 *    example: '- AX1234'
	 *  - delete specified subscriber
	 *    example: '- Ruby 301211074'
	 * 
	 * @param library
	 * @param command  
	 */		
	private static void handleRemoveCommand(Library library, String[] command) {
		if (command.length<3)
		{
			System.out.println("Invalid command");
			return;
		}		
		String last_name = command[1];
		String id = command[2];

		// Handle return book command	
		if (command.length == 4) {
			/*
			 * Example: - Baraq 43782322 AB1132
			 */
			String bookId = command[3];
			try {
				library.returnBook(last_name, id, bookId);
				System.out.println("Returned book!");
			} catch (IllegalArgumentException e) {
				System.out
						.println("Invalid subscriber id, name or book id");
			} catch (IllegalStateException e) {
				System.out.println("Illegal state exception: "
						+ e.getMessage());
			}
	    // Handle delete subscriber					
		} else if (command.length == 3) {
			
			/*
			 * Example: - Baraq 43782322
			 */
			try {
				library.delSubscriber(last_name, id);
				System.out.println("deleted subscriber!");
			} catch (IllegalArgumentException e) {
				System.out.println("Invalid subscriber id or name");
			}
		} else {
			System.out.println("Invalid command");
		}
	}

	/**
	 * Handles add('+') command
	 *  - borrow specified book
	 *    example: '+ Baraq 43782322 AX1234'
	 *  - add new subscriber
	 *    example: '+ Baraq 43782322'
	 * 
	 * @param library
	 * @param command  
	 */		
	private static void handleAddCommand(Library library, String[] command) {
		if (command.length<3)
		{
			System.out.println("Invalid command");
			return;
		}
		
		String last_name = command[1];
		String id = command[2];

		// Handle borrow book
		if (command.length == 4) {
			/*
			 * Example: + Baraq 43782322 AB1142
			 */
			String bookId = command[3];
			try {
				library.borrowBook(last_name, id, bookId);
				System.out.println("Borrowed book!");
			} catch (IllegalArgumentException e) {
				System.out
						.println("Invalid subscriber id, name or book id");
			} catch (IllegalStateException e) {
				System.out.println("Illegal state exception: "
						+ e.getMessage());
			}
		// Handle add subscriber					
		} else if (command.length == 3) {
			/*
			 * Example: + Baraq 43782322
			 */
			if (isBookCode(id)){
				System.out.println("Invalid id code");
				return;
			}
			try {
				library.addSubscriber(new Subscriber(id, last_name));
				System.out.println("Added new subscriber!");
			} catch (IllegalStateException e) {
				System.out.println("Illegal state exception: "
						+ e.getMessage());
			}
		} else {
			System.out.println("Invalid command");
		}
	}

	/**
	 * Checks if code match the pattern of [a-zA-Z]+[0-9]+
	 *  example:
	 * 	  'AB1234' - True
	 *    'ABCDE3' - False
	 *    'ABC'    - False
	 *    '123'    - False
	 * 
	 * @param code  
	 */			
	private static boolean isBookCode(String code) {
		if (Pattern.matches("[a-zA-Z]{2}[0-9]{4}", code))
			return true;
		else
			return false;
	}

}
